#pragma once
#ifndef __MEM_H
#define __MEM_H

#define MEMBYTE			unsigned char
#define BITS_PER_BYTE	8
#define BYTES_PER_INT	4

#define MIN(a, b)		((a < b)?a:b)
#define MAX(a, b)		((a > b)?a:b)

/**
 * as malloc, but returns (MEMBYTE*)
 * @param size
 */
MEMBYTE * memalloc(const int & size);

/**
 * @param which memory to free
 * @param optionally, how much if it there is to free
 */
void memfree(MEMBYTE * mem, const int & size);

/**
 * @param src "source memory" what to copy
 * @param dest "destination memory" where to copy to
 * @param length how much to copy
 * @return pointer to dest (modified after copy)
 */
MEMBYTE * memcopy(const MEMBYTE * src, MEMBYTE * dest, const int & len);

/**
 * @param mem what data to copy
 * @param len how much data to copy
 * @param newSize new size of allocation
 */
MEMBYTE * memrealloc(MEMBYTE * mem, const int & len, const int & newSize);

/**
 * @param src0 first memory source
 * @param src1 second memory source
 * @param length how much memory to compare
 * @return (src0[i:length] - src1[i:length]). difference between last non-equal MEMBYTEs. 0 if the memory values are identical.
 */
int memcompare(const MEMBYTE * src0, const MEMBYTE * src1, const int & length);

/**
 * @param src0 first memory source
 * @param len0 length of first memory source
 * @param src1 second memory source
 * @param len1 length of second memory source
 * @return (src0[i:length] - src1[i:length]). difference between last non-equal MEMBYTEs. 0 if the memory values are identical. value of MEMBYTE in src0 if src0 is longer. negative value of byte in src1 if src1 is longer
 */
int memcompare2(const MEMBYTE * src0, const int & len0, const MEMBYTE * src1, const int & len1);

/**
 * @param haystack where to search.
 * @param lenHaystack size of the haystack
 * @param needle what to search for.
 * @param lenNeedle size of the needle
 * @return the location of the needle found in haystack in memory
 */
MEMBYTE * memmem(MEMBYTE * haystack, const int & lenHaystack, const MEMBYTE * needle, const int & lenNeedle);

/**
 * @param mem0 where to get first data
 * @param len0 how much data is in the first data block
 * @param mem1 where to get second data
 * @param len1 how much data is in the second data block
 * @return a newly allocated block of memory that is len0+len1 MEMBYTEs big, containing mem0 and mem1 consecutivley (in that order)
 */
MEMBYTE * memconcat(const MEMBYTE * mem0, const int & len0, const MEMBYTE * mem1, const int & len1);
/**
 * @param filename the name of the file to load completley into memory
 * @param len a pointer to the integer where to put the file's size
 * @return allocated memory. 0 if data buffer was not correctly allocated
 */
MEMBYTE * memFromFile(const char * filename, int * len);

/**
 * @param data where to write to write from
 * @param length how much to write
 * filename where to write to (file in file system)
 */
int memToFile(const MEMBYTE * data, int & length, const char * filename);

/**
 * @param haystack which memory block to search
 * @param needle what value to search for
 * @param the index of the first instance of needle in haystack
 */
int memindexOf(const MEMBYTE * haystack, const int & haystackLen, const int & haystackStart, const MEMBYTE & needle);

void byteArrayOutput(const MEMBYTE * MEMBYTEs, const int & len);
int bitstoi(const MEMBYTE * data, const int & offsetBits, const int & bits);
int bytestoi(const MEMBYTE * data, const int & offset, const int & bytes);
void writeBits(const int & a_value, const int & a_numBits, 
			  MEMBYTE * data, const int & a_offsetBits);

void bitsToString(const MEMBYTE * data, const int & offsetBits, const int & bits, char * str, const int & strSize);

int STRLEN(const char * a_str);
int STRCMP(const char * a_str0, const char * a_str1);

int reverseBits(int & a_i, int & bitDepth);

#endif
